<?php
// +----------------------------------------------------------------------
// | zhanshop-cloud / Nginx.php    [ 2025/5/6 09:34 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2025 zhangqiquan All rights reserved.
// +----------------------------------------------------------------------
// | Author: zhangqiquan <768617998@qq.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace extend\stats;

use zhanshop\App;
use zhanshop\Helper;
use zhanshop\util\MaxFile;

class NginxRequest1000
{
    protected $statsLimit = 2000000;
    public function __construct()
    {
        $this->statsLimit = intval(App::env()->get('STATS_NGINX_REQUEST_LIMIT', '2000000'));
        if($this->statsLimit <= 0){
            $this->statsLimit = 2000000;
        }
    }
    /**
     * 统计top1000包含IP排行，uri排行
     * @param string $accessPath
     * @return void
     */
    public function stats(string $accessPath)
    {
        $ipStats = App::runtimePath().'/nginx_ip_stats';
        Helper::rmdir($ipStats);
        mkdir($ipStats);
        $number = 0;

        $uriStats = App::runtimePath().'/nginx_uri_stats';
        Helper::rmdir($uriStats);
        mkdir($uriStats);
        $ipLogs = [];
        $uriLogs = [];
        $requestCount = 0;
        $ipRequestCount = 0;
        foreach (App::make(MaxFile::class)->readLines($accessPath) as $v){
            if ($v){
                $arr = json_decode($v, true);
                if($arr){
                    $requestCount++;
                    $uri = $arr['host'].$arr['uri'];
                    $remoteAddr = $arr['remote_addr'];
                    $ipMod = Helper::ipMod($remoteAddr);
                    $uriMod = Helper::strMod($uri);
                    $uriLogs[$uriMod][] = $uri;
                    $ipLogs[$ipMod][] = $remoteAddr;
                    $number ++;
                    if($number >= 200000){
                        $number = 0;
                        $this->writeStats($ipStats, $ipLogs);
                        $this->writeStats($uriStats, $uriLogs);
                        $ipLogs = [];
                        $uriLogs = [];
                    }
                }
            }
        }
        if($ipLogs) $this->writeStats($ipStats, $ipLogs);
        if($uriLogs) $this->writeStats($uriStats, $uriLogs);

        $ipsCount = [];
        foreach (glob($ipStats.DIRECTORY_SEPARATOR. '*.log') as $file){
            $ips = $this->getTop1000($file, $ipRequestCount);
            foreach ($ips as $ip => $number){
                $ipsCount[$ip] = $number;
            }
        }
        arsort($ipsCount);
        $count = 0;
        $ip1000 = [];
        foreach ($ipsCount as $ip => $number){
            $ip1000[$ip] = $number;
            $count++;
            if($count >= 1000) break;
        }

        $urisCount = [];
        foreach (glob($uriStats.DIRECTORY_SEPARATOR. '*.log') as $file){
            $aaa = 0;
            $uris = $this->getTop1000($file, $aaa);
            foreach ($uris as $uri => $number){
                $urisCount[$uri] = $number;
            }
        }
        arsort($urisCount);
        $count = 0;
        $uri1000 = [];
        foreach ($urisCount as $uri => $number){
            $uri1000[$uri] = $number;
            $count++;
            if($count >= 1000) break;
        }
        Helper::rmdir($ipStats);
        Helper::rmdir($uriStats);
        return [
            'ip1000' => $ip1000,
            'uri1000' => $uri1000,
            'request_count' => $requestCount,
            'ip_count' => $ipRequestCount,
        ];
    }

    /**
     * 统计前1000个IP排行
     * @param string $file
     * @return array
     */
    protected function getTop1000(string $file, &$ipRequestCount)
    {
        $ips = [];
        foreach (App::make(MaxFile::class)->readLines($file) as $ip){
            if ($ip){
                $ips[$ip] = $ips[$ip] ?? 0;
                $ips[$ip] += 1;
            }
            if(count($ips) >= $this->statsLimit) break; // 避免内存溢出进行截断限制单个文件的IP统计
        }
        $ipRequestCount += count($ips);
        arsort($ips);
        $count = 0;
        $ip1000 = [];
        foreach ($ips as $ip => $number){
            $ip1000[str_replace(["\r", "\n"], "", $ip)] = $number;
            $count++;
            if($count >= 1000) break;
        }
        return $ip1000;
    }

    /**
     * 写入统计
     * @param string $path
     * @param array $logs
     * @return void
     */
    protected function writeStats(string $path, array $logs)
    {
        foreach ($logs as $ipMod => $log) {
            $log = implode("\n", $log);
            error_log($log."\n", 3, $path.'/'.$ipMod.'.log');
        }
    }
}